use std::str::FromStr;
use regex::Regex;

pub use super::instance::*;
pub use super::command::*;

#[derive(Debug, PartialEq)]
pub enum Statement {
    Instance(Instance),
    Command(Command),
}

#[derive(Debug, PartialEq)]
pub enum StatementErr {
    Empty,
    Err(String)
}

impl Statement {
    fn error(s: String) -> Result<Self, StatementErr> {
        Err(StatementErr::Err(s))
    }

    fn parse_command(s: &str) -> Result<Self, StatementErr> {
        // .probe v(n_out)
        let mut tokens = s.split_ascii_whitespace();
        let name = &tokens.next().unwrap()[1..];

        match name {
            "probe" => {
                match tokens.next() {
                    Some(node) => {
                        let re = Regex::new(r"^v\((.*?)\)$").unwrap();
                        if let Some(captures) = re.captures(node) {
                            if let Some(matched) = captures.get(1) {
                                Ok(Statement::Command(Command::Probe(matched.as_str().to_owned())))
                            } else {
                                Statement::error(format!("Invalid format in probe: '{}'", node))
                            }
                        } else {
                            Statement::error(format!("Invalid format in probe: '{}'", node))
                        }
                    } 
                    None => Statement::error("No node info in `probe` command".to_string())
                }
            }
            "end" => {
                Ok(Statement::Command(Command::End))
            }
            _ => {
                Statement::error(format!("Un support command '{}'", name))
            }
        }
    }

    fn parse_resistance(s: &str) -> Result<Self, StatementErr> {
        // rseg#x1 n_in1 n_1#x1 3
        let mut tokens = s.split_ascii_whitespace();

        let name = match tokens.next() {
            Some(token) => token.to_string(),
            None => return Statement::error("No name in resistance inst".to_string()),
        };

        let node1 = match tokens.next() {
            Some(token) => token.to_string(),
            None => return Statement::error("No node1 in resistance inst".to_string()),
        };

        let node2 = match tokens.next() {
            Some(token) => token.to_string(),
            None => return Statement::error("No node2 in resistance inst".to_string()),
        };

        let value = match tokens.next() {
            Some(token) => match Statement::parse_unit_string(token) {
                Some(value) => value,
                None => return Statement::error(
                    format!("Parse value '{}' failed in resistance inst", token)
                )
            }
            None => return Statement::error("No value in resistance inst".to_string()),
        };

        Ok(Statement::Instance(Instance::R(Resistance {
            name, node1, node2, value
        })))
    }

    fn parse_capacitance(s: &str) -> Result<Self, StatementErr> {
        // cseg#x3 n_out 0 4
        let mut tokens = s.split_ascii_whitespace();

        let name = match tokens.next() {
            Some(token) => token.to_string(),
            None => return Statement::error("No name in capacitance inst".to_string()),
        };

        let node1 = match tokens.next() {
            Some(token) => token.to_string(),
            None => return Statement::error("No node1 in capacitance inst".to_string()),
        };

        let node2 = match tokens.next() {
            Some(token) => token.to_string(),
            None => return Statement::error("No node2 in capacitance inst".to_string()),
        };

        let value = match tokens.next() {
            Some(token) => match Statement::parse_unit_string(token) {
                Some(value) => value,
                None => return Statement::error(
                    format!("Parse value '{}' failed in capacitance inst", token)
                )
            }
            None => return Statement::error("No value in capacitance inst".to_string()),
        };

        Ok(Statement::Instance(Instance::C(Capacitance {
            name, node1, node2, value
        })))
    }

    fn parse_inductance(s: &str) -> Result<Self, StatementErr> {
        // lseg#x2 n_1#x2 n_in3 2
        let mut tokens = s.split_ascii_whitespace();

        let name = match tokens.next() {
            Some(token) => token.to_string(),
            None => return Statement::error("No name in inductance inst".to_string()),
        };

        let node1 = match tokens.next() {
            Some(token) => token.to_string(),
            None => return Statement::error("No node1 in inductance inst".to_string()),
        };

        let node2 = match tokens.next() {
            Some(token) => token.to_string(),
            None => return Statement::error("No node2 in inductance inst".to_string()),
        };

        let value = match tokens.next() {
            Some(token) => match Statement::parse_unit_string(token) {
                Some(value) => value,
                None => return Statement::error(
                    format!("Parse value '{}' failed in inductance inst", token)
                )
            }
            None => return Statement::error("No value in inductance inst".to_string()),
        };

        Ok(Statement::Instance(Instance::L(Inductance {
            name, node1, node2, value
        })))
    }

    fn parse_voltage(input: &str) -> Result<Self, StatementErr> {
        // Vin N_in1 0 PULSE 0 3 1m 1m 1m 8m 20m
        // Vin N_in1 0 10
        // Vin N_in1 0 sin(0 3 0.2k)
        let mut tokens = input.split_ascii_whitespace();

        let name = match tokens.next() {
            Some(token) => token.to_string(),
            None => return Statement::error("No name in voltage inst".to_string()),
        };

        let node1 = match tokens.next() {
            Some(token) => token.to_string(),
            None => return Statement::error("No node1 in voltage inst".to_string()),
        };

        let node2 = match tokens.next() {
            Some(token) => token.to_string(),
            None => return Statement::error("No node2 in voltage inst".to_string()),
        };

        let token = match tokens.next() {
            Some(token) => token,
            None => return Statement::error("Invalid format in voltage inst".to_string()),
        };

        match token {
            "pulse" => {
                let low_voltage = match tokens.next() {
                    Some(token) => match Statement::parse_unit_string(token) {
                        Some(value) => value,
                        None => return Statement::error(
                            format!("Parse value '{}' failed in pulse voltage inst", token)
                        )
                    }
                    None => return Statement::error("No low_voltage in pulse voltage inst".to_string()),
                };

                let high_voltage = match tokens.next() {
                    Some(token) => match Statement::parse_unit_string(token) {
                        Some(value) => value,
                        None => return Statement::error(
                            format!("Parse value '{}' failed in pulse voltage inst", token)
                        )
                    }
                    None => return Statement::error("No high_voltage in pulse voltage inst".to_string()),
                };

                let delay = match tokens.next() {
                    Some(token) => match Statement::parse_unit_string(token) {
                        Some(value) => value,
                        None => return Statement::error(
                            format!("Parse value '{}' failed in pulse voltage inst", token)
                        )
                    }
                    None => return Statement::error("No delay in pulse voltage inst".to_string()),
                };


                let rise_time = match tokens.next() {
                    Some(token) => match Statement::parse_unit_string(token) {
                        Some(value) => value,
                        None => return Statement::error(
                            format!("Parse value '{}' failed in pulse voltage inst", token)
                        )
                    }
                    None => return Statement::error("No rise_time in pulse voltage inst".to_string()),
                };

                let fall_time = match tokens.next() {
                    Some(token) => match Statement::parse_unit_string(token) {
                        Some(value) => value,
                        None => return Statement::error(
                            format!("Parse value '{}' failed in pulse voltage inst", token)
                        )
                    }
                    None => return Statement::error("No fall_time in pulse voltage inst".to_string()),
                };


                let pulse_width = match tokens.next() {
                    Some(token) => match Statement::parse_unit_string(token) {
                        Some(value) => value,
                        None => return Statement::error(
                            format!("Parse value '{}' failed in pulse voltage inst", token)
                        )
                    }
                    None => return Statement::error("No pulse_width in pulse voltage inst".to_string()),
                };

                let period = match tokens.next() {
                    Some(token) => match Statement::parse_unit_string(token) {
                        Some(value) => value,
                        None => return Statement::error(
                            format!("Parse value '{}' failed in pulse voltage inst", token)
                        )
                    }
                    None => return Statement::error("No period in pulse voltage inst".to_string()),
                };

                Ok(Statement::Instance(Instance::V(Voltage {
                    name, node1, node2, kind: VoltageKind::Pulse(PulseVoltageKind {
                        low_voltage, high_voltage, delay, rise_time, fall_time, pulse_width, period
                    })
                })))
            }
            s if s.starts_with("sin") => {
                let left_pos = match input.find('(') {
                    Some(left_pos) => left_pos,
                    None => return Statement::error("Invalid format in sin voltage inst, no '('".to_string()),
                };
                let right_pos = match input.find(')') {
                    Some(right_pos) => right_pos,
                    None => return Statement::error("Invalid format in sin voltage inst, no ')'".to_string()),
                };

                let sub_str = &input[left_pos + 1..right_pos];
                let mut tokens = sub_str.split_ascii_whitespace();
                
                let bias = match tokens.next() {
                    Some(token) => match Statement::parse_unit_string(token) {
                        Some(value) => value,
                        None => return Statement::error(
                            format!("Parse value '{}' failed in sin voltage inst", token)
                        )
                    }
                    None => return Statement::error("No bias in sin voltage inst".to_string()),
                };

                let amplitude = match tokens.next() {
                    Some(token) => match Statement::parse_unit_string(token) {
                        Some(value) => value,
                        None => return Statement::error(
                            format!("Parse value '{}' failed in sin voltage inst", token)
                        )
                    }
                    None => return Statement::error("No amplitude in sin voltage inst".to_string()),
                };

                let frequence = match tokens.next() {
                    Some(token) => match Statement::parse_unit_string(token) {
                        Some(value) => value,
                        None => return Statement::error(
                            format!("Parse value '{}' failed in sin voltage inst", token)
                        )
                    }
                    None => return Statement::error("No frequence in sin voltage inst".to_string()),
                };

                Ok(Statement::Instance(Instance::V(Voltage {
                    name, node1, node2, kind: VoltageKind::Sin(SinVoltageKind {
                        bias, amplitude, frequence 
                    })
                })))
            }
            _ => {
                match Statement::parse_unit_string(token) {
                    Some(value) => {
                        Ok(Statement::Instance(Instance::V(Voltage {
                            name, node1, node2, kind: VoltageKind::DC(DCVoltageKind {
                                value
                            })
                        })))
                    },
                    None => Statement::error(
                        format!("Parse value '{}' failed in DC voltage inst", token)
                    )
                }
            }
        }
    }

    fn parse_current(s: &str) -> Result<Self, StatementErr> {
        // lseg#x2 n_1#x2 n_in3 2
        let mut tokens = s.split_ascii_whitespace();

        let name = match tokens.next() {
            Some(token) => token.to_string(),
            None => return Statement::error("No name in current inst".to_string()),
        };

        let node1 = match tokens.next() {
            Some(token) => token.to_string(),
            None => return Statement::error("No node1 in current inst".to_string()),
        };

        let node2 = match tokens.next() {
            Some(token) => token.to_string(),
            None => return Statement::error("No node2 in current inst".to_string()),
        };

        let value = match tokens.next() {
            Some(token) => match Statement::parse_unit_string(token) {
                Some(value) => value,
                None => return Statement::error(
                    format!("Parse value '{}' failed in current inst", token)
                )
            }
            None => return Statement::error("No value in current inst".to_string()),
        };

        Ok(Statement::Instance(Instance::I(Current {
            name, node1, node2, value
        })))
    }
}

impl FromStr for Statement {
    type Err = StatementErr;

    fn from_str(s: &str) -> Result<Self, StatementErr> {
        let s = s.trim();
        if s.is_empty() || s.starts_with('*') {
            // "    /t /a"
            // "  \t * just a command"
            return Err(StatementErr::Empty);
        }

        let s = s.to_lowercase();
        
        let first_ch = s.chars().nth(0).unwrap();
        match s.chars().nth(0).unwrap() {
            '.' => Statement::parse_command(&s),
            'r' => Statement::parse_resistance(&s),
            'c' => Statement::parse_capacitance(&s),
            'l' => Statement::parse_inductance(&s),
            'v' => Statement::parse_voltage(&s),
            'i' => Statement::parse_current(&s),
            _ => Err(StatementErr::Err(format!("Un support statement start with '{}'", first_ch)))
        }
    }
}

impl Statement {
    fn parse_unit_string(input: &str) -> Option<f64> {
        let re = Regex::new(r"^([+-]?\d*\.?\d+)([munpk]?)$").unwrap();
        if let Some(caps) = re.captures(input) {
            let value: f64 = caps[1].parse().ok()?;
            let multiplier = match &caps[2] {
                "m" => 1e-3,  // 毫
                "u" => 1e-6,  // 微
                "n" => 1e-9,  // 纳
                "p" => 1e-12, // 皮
                "k" => 1e3,   // 千
                _ => 1.0,     // 默认没有单位
            };
            return Some(value * multiplier);
        }
        None
    }
}

#[allow(unused)]
mod test {
    use super::*;

    #[test]
    fn test_parse_unit_string() {
        // let inputs = vec!["6m", "12.432u", "1.5n", "-4p", "100", "+3.4u", "5k"];
        // let outputs = vec![6e-3, 12.432e-6, 1.5e-9, -4e-12, 100., 3.4e-6, 5e3];
        // for (input, output) in inputs.into_iter().zip(outputs.into_iter()) {
        //     match Statement::parse_unit_string(input) {
        //         Some(value) => assert_eq!(value, output),
        //         None => println!("Failed to parse {}", input),
        //     }
        // }
    }

    #[test]
    fn test_parse_command() {
        assert_eq!(
            Statement::parse_command(".probe  v(n_out)"),
            Ok(Statement::Command(Command::Probe("n_out".to_string())))
        );
    }

    #[test]
    fn test_parse_resistance() {
        assert_eq!(
            Statement::parse_resistance("rseg#x3 n_in3 n_1#x3 3"),
            Ok(Statement::Instance(Instance::R(Resistance{
                name: "rseg#x3".to_string(),
                node1: "n_in3".to_string(),
                node2: "n_1#x3".to_string(),
                value: 3.0,
            })))
        );
    }

    #[test]
    fn test_parse_voltage() {
        assert_eq!(
            Statement::parse_voltage("Vin N_in1 0 pulse 0 3 1m 1m 1m 8m 20m"),
            Ok(Statement::Instance(Instance::V(Voltage {
                name: "Vin".to_string(),
                node1: "N_in1".to_string(),
                node2: "0".to_string(),
                kind: VoltageKind::Pulse(PulseVoltageKind {
                    low_voltage: 0.,
                    high_voltage: 3.,
                    delay: 1e-3,
                    rise_time: 1e-3,
                    fall_time: 1e-3,
                    pulse_width: 8e-3,
                    period: 20e-3
                })
            })))
        );

        assert_eq!(
            Statement::parse_voltage("vin n_in1 0 10"),
            Ok(Statement::Instance(Instance::V(Voltage {
                name: "vin".to_string(),
                node1: "n_in1".to_string(),
                node2: "0".to_string(),
                kind: VoltageKind::DC(DCVoltageKind {
                    value: 10.,
                })
            })))
        );

        assert_eq!(
            Statement::parse_voltage("vin n_in1 0 sin(0 3 0.2k)"),
            Ok(Statement::Instance(Instance::V(Voltage {
                name: "vin".to_string(),
                node1: "n_in1".to_string(),
                node2: "0".to_string(),
                kind: VoltageKind::Sin(SinVoltageKind {
                    bias: 0.,
                    amplitude: 3.,
                    frequence: 200.
                })
            })))
        );
    }
}